import os

import xlrd
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group
from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Q
from django.utils.decorators import method_decorator
from rest_framework import exceptions
from rest_framework import mixins
from rest_framework import status
from rest_framework.permissions import DjangoModelPermissions
from rest_framework.response import Response
from rest_framework.viewsets import GenericViewSet, ModelViewSet
from xlrd.biffh import XLRDError

from FasterRunner import pagination
from FasterRunner.settings import MEDIA_ROOT, logger
from fastrunner import models, serializers
from fastrunner.models import Project, ProjectGroup
from fastrunner.utils import prepare
from fastrunner.utils import response
from fastrunner.utils.decorator import request_log
from fastrunner.utils.permissions import IsBelongToProject, _check_is_locked
from fastrunner.utils.runner import DebugCode
from fastrunner.utils.tree import get_tree_max_id

UserModel = get_user_model()


class ProjectView(ModelViewSet):
    """
    项目增删改查
    """
    serializer_class = serializers.ProjectSerializer
    pagination_class = pagination.MyCursorPagination
    permission_classes = (DjangoModelPermissions, IsBelongToProject)

    def get_queryset(self):
        if self.request.user.is_superuser:
            pj_instance, b = Project.objects.get_or_create(pk=1,defaults={'name': '平台项目', 'desc': '系统初始化一个平台项项目'})
            return models.Project.objects.all().exclude(pk=1).order_by('responsible', '-id')
        # 根据user_id 取得project_id 列表
        groups = Group.objects.filter(user=self.request.user)
        belong_projects = ProjectGroup.objects.filter(pk__in=groups).values_list("belong_project", flat=True)
        belong_list = [x for x in belong_projects]
        # 根据关键字 取得 project_id 列表
        show_project_list = Project.objects.filter(name__contains='演示').values_list('id', flat=True)
        show_list = [x for x in show_project_list]
        # 待返回project_id 列表
        project_list = belong_list + show_list
        return models.Project.objects.filter(Q(id__in=[id for id in project_list])).order_by('responsible', '-id')

    @method_decorator(request_log(level='INFO'))
    def single(self, request, **kwargs):
        """
        得到单个项目相关统计信息
        """
        pk = kwargs.pop('pk')
        try:
            queryset = models.Project.objects.get(id=pk)
        except ObjectDoesNotExist:
            return Response(response.PROJECT_NOT_EXISTS)

        serializer = self.get_serializer(queryset, many=False)

        project_info = prepare.get_project_detail(pk)
        project_info.update(serializer.data)
        return Response(project_info)

    def perform_create(self, serializer):
        if self.request.user.is_superuser:
            instance = serializer.save()
            # user = UserModel.objects.get(id=self.request.user.id)
            # user.belong_project.add(instance)
            # 初始化项目
            prepare.project_init(instance)
        else:
            return Response(response.PROJECT_NOT_EXISTS)

    @logger.catch
    def perform_destroy(self, instance):
        # 销毁项目相关数据
        print("self.request.user.is_superuser:",self.request.user.is_superuser)
        if self.request.user.is_superuser:
            prepare.project_end(instance)
            instance.delete()


class DashboardView(GenericViewSet):
    """
    dashboard信息
    """

    @method_decorator(request_log(level='INFO'))
    def get(self, request, **kwargs):
        return Response(prepare.get_project_detail(kwargs['pk']))


class TreeView(GenericViewSet):
    """
    树形结构操作
    """

    permission_classes = (DjangoModelPermissions, IsBelongToProject)

    def get_queryset(self):
        project_id = self.kwargs.get('pk')
        queryset = models.Relation.objects.filter(project__id=project_id).order_by('-update_time')
        return queryset

    @method_decorator(request_log(level='INFO'))
    def get(self, request, **kwargs):
        """
        返回树形结构
        当前最带节点ID
        """

        try:
            tree_type = request.query_params['type']
        except KeyError:
            return Response(response.KEY_MISS)
        try:
            tree = models.Relation.objects.get(project_id=kwargs['pk'], type=tree_type)
            body = eval(tree.tree)
        except ObjectDoesNotExist as e:
            tree = models.Relation.objects.create(project_id=kwargs['pk'], type=tree_type, tree=[{'id': 1, 'label': 'testdata', 'children': []}])
            body = []

        tree = {
            "tree": body,
            "id": tree.id,
            "success": True,
            "max": get_tree_max_id(body)
        }
        return Response(tree)

    @method_decorator(request_log(level='INFO'))
    def patch(self, request, **kwargs):
        """
        修改树形结构，ID不能重复
        """
        try:
            body = request.data['body']
            mode = request.data['mode']

            relation = models.Relation.objects.get(id=kwargs['pk'])
            relation.tree = body
            relation.save()

        except KeyError:
            return Response(response.KEY_MISS)

        except ObjectDoesNotExist:
            return Response(response.SYSTEM_ERROR)

        #  mode -> True remove node
        if mode:
            prepare.tree_end(request.data, relation.project)

        response.TREE_UPDATE_SUCCESS['tree'] = body
        response.TREE_UPDATE_SUCCESS['max'] = get_tree_max_id(body)

        return Response(response.TREE_UPDATE_SUCCESS)


class FileView(GenericViewSet, mixins.CreateModelMixin, mixins.ListModelMixin, mixins.DestroyModelMixin):
    """
    list:当前项目文件列表
    create:上传与更新文件
    destroy:删除文件
    """
    serializer_class = serializers.FileSerializer
    pagination_class = pagination.MyPageNumberPagination

    permission_classes = (DjangoModelPermissions, IsBelongToProject)

    def get_queryset(self):
        if self.action == 'create':
            project = self.request.data['project']
            name = self.request.data['name']
            return models.ModelWithFileField.objects.filter(project__id=project, name=name).order_by('-update_time')
        else:
            project = self.request.query_params['project']
            queryset = models.ModelWithFileField.objects.filter(project__id=project).order_by('-update_time')
            if self.action == 'list':
                node = self.request.query_params.get('node', '')
                search = self.request.query_params.get('search', '')
                if search != '':
                    queryset = queryset.filter(name__contains=search)
                if node != '':
                    queryset = queryset.filter(relation=node)
            return queryset

    def create(self, request, *args, **kwargs):
        if not self.get_queryset():
            serializer = self.get_serializer(data=request.data)
            serializer.is_valid(raise_exception=True)
            self.perform_create(serializer)
            headers = self.get_success_headers(serializer.data)
            return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
        else:
            self.kwargs['pk'] = self.get_queryset()[0].id
            _check_is_locked(request.data['project'], 1, self.kwargs['pk'])

            instance = self.get_object()
            filepath = os.path.join(MEDIA_ROOT, str(instance.file))
            if os.path.exists(filepath):
                os.remove(filepath)
            req_relation = request.data.get("relation", '')
            if not req_relation:
                request.data["relation"] = instance.relation

            partial = kwargs.pop('partial', False)
            serializer = self.get_serializer(instance, data=request.data, partial=partial)
            serializer.is_valid(raise_exception=True)
            self.perform_create(serializer)
            if getattr(instance, '_prefetched_objects_cache', None):
                # If 'prefetch_related' has been applied to a queryset, we need to
                # forcibly invalidate the prefetch cache on the instance.
                instance._prefetched_objects_cache = {}

            return Response(serializer.data)

    def destroy(self, request, *args, **kwargs):
        if kwargs.get('pk') and int(kwargs['pk']) != -1:
            _check_is_locked(request.query_params['project'], 1, kwargs['pk'])
            instance = self.get_object()
            filepath = os.path.join(MEDIA_ROOT, str(instance.file))
            if os.path.exists(filepath):
                os.remove(filepath)
            self.perform_destroy(instance)
        elif request.data:
            for content in request.data:
                self.kwargs['pk'] = content['id']
                try:
                    _check_is_locked(content['project'], 1, content['id'])
                except exceptions.NotAcceptable as e:
                    continue
                instance = self.get_object()
                filepath = os.path.join(MEDIA_ROOT, str(instance.file))
                if os.path.exists(filepath):
                    os.remove(filepath)
                self.perform_destroy(instance)
        return Response(status=status.HTTP_204_NO_CONTENT)

    def perform_create(self, serializer):
        serializer.save()
        try:
            excel_file = models.ModelWithFileField.objects.get(id=serializer.data["id"])
            file_path = os.path.join(MEDIA_ROOT, str(excel_file.file))
            excel_info = xlrd.open_workbook(file_path)
            excel_tree = {"value": excel_file.name, "label": excel_file.name, "children": []}
            for sheet in excel_info.sheets():
                excel_tree["children"].append({"value": sheet.name, "label": sheet.name})
            excel_file.excel_tree = excel_tree
            excel_file.save()
        except XLRDError as e:
            pass


class PycodeRunView(GenericViewSet, mixins.RetrieveModelMixin):
    """
    驱动代码调试运行
    """
    serializer_class = serializers.PycodeSerializer

    permission_classes = (DjangoModelPermissions, IsBelongToProject)

    def get_queryset(self):
        project = self.request.query_params["project"]
        queryset = models.Pycode.objects.filter(project_id=project).order_by('-update_time')
        return queryset

    @method_decorator(request_log(level='INFO'))
    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        debug = DebugCode(serializer.data["code"], serializer.data["project"], serializer.data["name"])
        debug.run()

        debug_rsp = {
            "msg": debug.resp
        }
        return Response(data=debug_rsp)


class PycodeView(ModelViewSet):
    """
    驱动代码模块
    """
    serializer_class = serializers.PycodeSerializer
    pagination_class = pagination.MyPageNumberPagination

    permission_classes = (DjangoModelPermissions, IsBelongToProject)

    def get_queryset(self):
        project = self.request.query_params["project"]
            # 初始化一个平台项目，用于存放平台驱动
        pj_instance, b = Project.objects.get_or_create(pk=1, defaults={'name': '平台项目', 'desc': '系统初始化一个平台项项目'})

        queryset = models.Pycode.objects.filter(project_id=project).order_by('-update_time')
        if self.action == 'list':
            queryset = queryset.filter(name__contains=self.request.query_params["search"])
        elif self.action in ('partial_update', 'create'):
            # 判断是否超级管理，否则无权编辑平台驱动代码
            if int(project) == 1 and not self.request.user.is_superuser:
                queryset = queryset.filter(project_id=-1)
        else:
            print(self.action)
        return queryset

    @method_decorator(request_log(level='INFO'))
    def destroy(self, request, *args, **kwargs):
        project = self.request.query_params["project"]
        # 单个驱动代码删除
        if kwargs.get('pk') and int(kwargs['pk']) != -1:
            instance = self.get_object()
            if instance.name == 'debugtalk.py':
                Response(status=status.HTTP_423_LOCKED)
            elif int(project) == 1 and not self.request.user.is_superuser:
                # 非管理员，不能删除平台驱动，只能删除项目驱动
                print("非管理员，不能删除平台驱动，只能删除项目驱动")
                Response(status=status.HTTP_204_NO_CONTENT)
            else:
                self.perform_destroy(instance)
        # 批量删除时，kwargs.get('pk')=='-1'
        elif request.data:
            for content in request.data:
                self.kwargs['pk'] = content['id']
                instance = self.get_object()
                if instance.name != 'debugtalk.py':
                    self.perform_destroy(instance)
        return Response(status=status.HTTP_204_NO_CONTENT)
